package com.treepoke.util.date;

import com.treepoke.util.string.JustificacionUtil;
import java.math.BigDecimal;
import java.sql.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;

/**
 * Clase que realiza tareas de conversion entre fechas.
 */
public class DateUtil {
    
    public final static String PATRON_FECHA_1 = "dd/MM/yyyy";
    public final static String PATRON_FECHA_2 = "yyyyMMdd";
    public final static String PATRON_FECHA_3 = "yyyy-MM-dd";
    public final static String PATRON_FECHA_4 = "MM/dd/yyyy";
    public final static String PATRON_FECHA_5 = "yyMMdd";
    public final static String PATRON_FECHA_6 = "ddMMyy";
    public final static String PATRON_HORA_1 = "HH:mm:ss";
    public final static String PATRON_HORA_2 = "hh:mm a";
    public final static String PATRON_FECHA_HORA_1 = "MM/dd/yyyy HH:mma";
    public final static String PATRON_FECHA_HORA_2 = "yyyyMMddHHmmssms";
    public final static String PATRON_FECHA_HORA_3 = "ddMMyy_HHmmss";
    public final static String PATRON_FECHA_HORA_4 = "dd/MM/yyyy HH:mm:ss";
    public final static String PATRON_FECHA_HORA_5 = "yyyy-MM-dd HH:mm:ss";
    /**
     * Simple Date formatter para formatear fechas de Calendar a numericco
     */
    private static SimpleDateFormat dateConverter = new SimpleDateFormat(
            PATRON_FECHA_2);
    /**
     * Simple Date formatter para formatear fechas de Calendar a numericco
     */
    private static SimpleDateFormat hourConverter = new SimpleDateFormat(
            PATRON_HORA_1);
    private static final SimpleDateFormat dateHourConverter = new SimpleDateFormat(
            PATRON_FECHA_HORA_1);
    private static final SimpleDateFormat dateHourConverter2 = new SimpleDateFormat(
            PATRON_FECHA_HORA_2);

    /**
     * Obtiene un String con el formato MM/dd/yyyy HH:mmAM/PM a partir de una
     * fecha.
     *
     * @param fechaAConvertir
     * @return
     */
    public static String getFechaHora(Calendar fechaAConvertir) {
        return dateHourConverter.format(fechaAConvertir.getTime());
    }

    /**
     * Obtiene una fecha de tipo numerico a partir de un Calendar.
     *
     * @param dateToConvert Calendar a convertir.
     * @return
     */
    public static BigDecimal getNumberFromDate(Calendar dateToConvert) {
        return new BigDecimal(dateConverter.format(dateToConvert.getTime()));
    }

    /**
     * Obtiene una fecha de tipo entero a partir de un Calendar.
     *
     * @param dateToConvert Calendar a convertir.
     * @return
     */
    public static Integer getIntegerFromDate(Calendar dateToConvert) {
        return new Integer(dateConverter.format(dateToConvert.getTime()));
    }

    /**
     * Obtiene una fecha en formato Calendar a partir de un numero.
     *
     * @param numberToConvert Numero a convertir en formato yyyyMMdd
     * @return
     * @throws ParseException
     */
    public static Calendar getDateFromNumber(BigDecimal numberToConvert)
            throws ParseException {
        Calendar dateToReturn = Calendar.getInstance();
        dateToReturn.setTime(dateConverter.parse(numberToConvert.toString()));
        return dateToReturn;
    }

    /**
     * Obtiene una fecha en formato Calendar a partir de un numero.
     *
     * @param numberToConvert Numero a convertir en formato yyyyMMdd
     * @return
     * @throws ParseException
     */
    public static Calendar getDateFromNumber(Integer numberToConvert)
            throws ParseException {
        Calendar dateToReturn = Calendar.getInstance();
        dateToReturn.setTime(dateConverter.parse(numberToConvert.toString()));
        return dateToReturn;
    }

    /**
     * Retorna una fecha en formato TimeStamp a partir de un Calendar.
     *
     * @param dateToConvert Fecha a convertir
     * @return
     * @throws ParseException
     */
    public static Timestamp getTimestampFromCalendar(Calendar dateToConvert) {
        return new Timestamp(dateToConvert.getTimeInMillis());
    }

    /**
     * Retorna una fecha en formato TimeStamp a partir de un Date.
     *
     * @param dateToConvert Fecha a convertir
     * @return
     * @throws ParseException
     */
    public static Timestamp getTimestampFromDate(Date dateToConvert) {
        return new Timestamp(dateToConvert.getTime());
    }

    /**
     * Retorna una fecha en formato java.sql.TimeStamp SQL TimeStamp a partir de
     * un Calendar.
     *
     * @param dateToConvert Fecha a convertir
     * @return
     * @throws ParseException
     */
    public static java.sql.Timestamp getSQLTimestampFromCalendar(Calendar dateToConvert) {
        return new java.sql.Timestamp(dateToConvert.getTimeInMillis());
    }

    /**
     * Obtiene un objeto de tipo Calendar desde un TimeStamp.
     *
     * @param timestampToConvert
     * @return
     * @throws ParseException
     */
    public static Calendar getCalendarFromTimestamp(Timestamp timestampToConvert) {
        Calendar dateToReturn = Calendar.getInstance();
        dateToReturn.setTimeInMillis(timestampToConvert.getTime());
        return dateToReturn;
    }

    /**
     * Permite obtener la hora de un Calendar en formato HH:mm:ss.
     *
     * @param fecha
     * @return
     */
    public static String obtenerHoraDeCalendar(Calendar fecha) {
        return hourConverter.format(fecha.getTime());
    }

    /**
     * Devuelve la hora en formato HH:mm:ss según la hora, minuto y segundo
     * enviados como parámetro
     *
     * @param hora
     * @param minuto
     * @param segundo
     * @return
     */
    public static String getHourFromIntegers(Integer hora, Integer minuto, Integer segundo) {

        return JustificacionUtil.completarLongitudCampoIzquierda(hora.toString(), 2, "0") + ":"
                + JustificacionUtil.completarLongitudCampoIzquierda(minuto.toString(), 2, "0") + ":"
                + JustificacionUtil.completarLongitudCampoIzquierda(segundo.toString(), 2, "0");
    }

    /**
     * Devuelve un entero largo que representa la hora en formato HH:mm:ss
     * enviada como parámetro
     *
     * @param hourToConvert
     * @return
     */
    public static Long getLongFromHour(String hourToConvert) throws ParseException {
        return hourConverter.parse(hourToConvert).getTime();
    }

    /**
     * Devuelve una cadena de texto que representa la fecha y el patrón enviados
     * como parámetro
     *
     * @param dateToConvert
     * @param pattern
     * @return
     */
    public static String getStringFromDate(Calendar dateToConvert, String pattern) {
        return (new SimpleDateFormat(pattern)).format(dateToConvert.getTime());
    } 
    
    /**
     * Devuelve una cadena de texto que representa la fecha y el patrón enviados
     * como parámetro
     *
     * @param dateToConvert
     * @param pattern
     * @return
     */
    public static String getStringFromDate(Date dateToConvert, String pattern) {
        return (new SimpleDateFormat(pattern)).format(dateToConvert);
    }

     /**
     * Devuelve una cadena de texto que representa la fecha y el patrón enviados
     * como parámetro
     *
     * @param dateToConvert
     * @param pattern
     * @return
     */
    public static String getStringFromDateValue(Date dateToConvert, String pattern) {
        return (new SimpleDateFormat(pattern)).format(dateToConvert.getTime());
    }

    /**
     * Devuelve una fecha a partir de la cadena y el patrón enviados como
     * parámetro
     *
     * @param stringToConvert
     * @param pattern
     * @return
     */
    public static Calendar getCalendarFromString(String stringToConvert, String pattern) throws ParseException {
        return getCalendarFromDate((new SimpleDateFormat(pattern)).parse(stringToConvert));
    }

    /**
     * Devuelve un calendar a partir de un date     *
     * @param dateToConvert
     * @return
     */
    public static Calendar getCalendarFromDate(Date dateToConvert) {
        if (dateToConvert == null) {
            return null;
        }
        
        Calendar c = Calendar.getInstance();
        c.setTime(dateToConvert);
        return c;
    }

    /**
     * Devuelve una cadena representado una hora en un formato destino según un
     * formato fuente.
     * @param hourToConvert
     * @param srcPattern
     * @param desPattern
     * @return
     */
    public static String getStringFromHour(String hourToConvert, String srcPattern, String desPattern) throws ParseException {
        return (new SimpleDateFormat(desPattern)).format((new SimpleDateFormat(srcPattern)).parse(hourToConvert));
    }

    /**
     * Devuelve un objecto Calendar que representa la siguiente fecha y hora
     * respecto a la fecha actual, según la hora en formato HH:mm:ss enviada
     * como parámetro 
     * @param hourToConvert
     * @return
     */
    public static Calendar getNextCalendarFromHour(String hourToConvert) throws ParseException {
        Calendar calendar = null;

        calendar = Calendar.getInstance();
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        Calendar hourCalendar = Calendar.getInstance();
        hourCalendar.setTime(hourConverter.parse(hourToConvert));
        Long hourLong = getLongFromHour(hourToConvert);
        Long currentHourLong = getLongFromHour(obtenerHoraDeCalendar(calendar));
        if (hourLong <= currentHourLong) {
            calendar.add(Calendar.DATE, 1);
        }
        calendar.set(Calendar.HOUR_OF_DAY, hourCalendar.get(Calendar.HOUR_OF_DAY));
        calendar.set(Calendar.MINUTE, hourCalendar.get(Calendar.MINUTE));
        calendar.set(Calendar.SECOND, hourCalendar.get(Calendar.SECOND));
        calendar.set(Calendar.MILLISECOND, hourCalendar.get(Calendar.MILLISECOND));
 
        return calendar;
    }
    
    /**
     * This function return a XMLGregoriaCalendar given a date
     * @param date fecha que se desea convertir
     * @return XMLGregorianCalendar
     * @throws javax.xml.datatype.DatatypeConfigurationException
     */
    public static XMLGregorianCalendar date2XMLGregorianCalendar(Date date) throws DatatypeConfigurationException { 
        Calendar c = new GregorianCalendar(); 
        c.setTime(date); 
        int month = c.get(Calendar.MONTH)+1; 
        int day = c.get(Calendar.DAY_OF_MONTH); 
        int year = c.get(Calendar.YEAR); 
        int hour = c.get(Calendar.HOUR_OF_DAY); 
        int second = c.get(Calendar.SECOND); 
        int minute = c.get(Calendar.MINUTE); 
        int millisecond = c.get(Calendar.MILLISECOND); 
        DatatypeFactory df = DatatypeFactory.newInstance();         
        XMLGregorianCalendar x = df.newXMLGregorianCalendar(year, month, day, hour,minute,second,millisecond,c.getTimeZone().getOffset(c.getTimeInMillis())/(60*60*1000)); 
        return x; 
    }
    
    /**
     * Método que retorna la fecha dada con las horas, minutos, segundos y milisegundos en su valor inicial
     * @return La fecha dada con las horas, minutos, segundos y milisegundos en su valor inicial
     * @param calendar
     * @author ELTV
     */	
    public static Calendar getFechaInicialHMS(Calendar calendar) {
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        return calendar;
    }	

    /**
     * Método que retorna la fecha dada con las horas, minutos, segundos y milisegundos en su valor final
     * @return La fecha dada con las horas, minutos, segundos y milisegundos en su valor final
     * @param calendar
     * @author ELTV
     */	
    public static Calendar getFechaFinalHMS(Calendar calendar) {
        calendar.set(Calendar.HOUR_OF_DAY, 23);
        calendar.set(Calendar.MINUTE, 59);
        calendar.set(Calendar.SECOND, 59);
        calendar.set(Calendar.MILLISECOND, 999);
        return calendar;
    }
	
    /**
     * Recibe el objeto Date y lo asigna a un objeto Calendar para llamar al método getFechaFinalHMS(Calendar calendar)
     * @return 
     * @param fecha
     * @author
     */
    public static Calendar getFechaInicialHMS(Date fecha) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(fecha);
        return getFechaInicialHMS(calendar);
    }    

    /**
     * Recibe el objeto Date y lo asigna a un objeto Calendar para llamar al método getFechaFinalHMS(Calendar calendar)
     * @return 
     * @param fecha
     * @author
     */
    public static Calendar getFechaFinalHMS(Date fecha) {
      Calendar calendar = Calendar.getInstance();
      calendar.setTime(fecha);
      return getFechaFinalHMS(calendar);
    }
    
    /**
     * Método que retorna la fecha dada con las horas, minutos, segundos y milisegundos en su valor inicial
     * @return 
     * @param fecha
     * @author ELTV
     */
    public static Date getFechaInicialDateHMS(Date fecha) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(fecha);
        return getFechaInicialHMS(calendar).getTime();
    }    

    /**
     * Método que retorna la fecha dada con las horas, minutos, segundos y milisegundos en su valor final
     * @return 
     * @param fecha
     * @author ELTV
     */
    public static Date getFechaFinalDateHMS(Date fecha) {
      Calendar calendar = Calendar.getInstance();
      calendar.setTime(fecha);
      return getFechaFinalHMS(calendar).getTime();
    }
    
    /**
     * Obtiene un String con el formato yyyyMMddHHmma a partir de una
     * fecha.
     *
     * @param fechaAConvertir
     * @return
     */
    public static String getFechaHoraCalendar(Calendar fechaAConvertir) {
        return dateHourConverter2.format(fechaAConvertir.getTime());
    }
}
